#include "Box.h"
#include <iostream>
#include "glm/gtx/string_cast.hpp"

#define INT_SGN(x) ((float)(((0 < x) - (x < 0))))

using namespace std;

glm::vec3 max_among(glm::vec3 v1, glm::vec3 v2) {
    glm::vec3 v;

    v = v1;

    (v.x < v2.x) && (v.x = v2.x);
    (v.y < v2.y) && (v.y = v2.y);
    (v.z < v2.z) && (v.z = v2.z);

    return v;
}

glm::vec3 min_among(glm::vec3 v1, glm::vec3 v2) {
    glm::vec3 v;

    v = v1;
    (v.x > v2.x) && (v.x = v2.x);
    (v.y > v2.y) && (v.y = v2.y);
    (v.z > v2.z) && (v.z = v2.z);

    return v;
}

float smallest_positive_valid(int num, float *arr, glm::vec3 p, glm::vec3 d,
 glm::vec3 min, glm::vec3 max) {
    int i;
    float m = -1;
    glm::vec3 val;

    for (i = 0; i < num; i++) {
        val = p + arr[i] * d;
        if (
         val.x < min.x - 0.0001 || val.x > max.x + 0.0001 ||
         val.y < min.y - 0.0001 || val.y > max.y + 0.0001 ||
         val.z < min.z - 0.0001 || val.z > max.z + 0.0001)
            continue;

        if (m == -1 && arr[i] > 0.f)
            m = arr[i];
        else if (arr[i] > 0.f && arr[i] < m)
            m = arr[i];
    }

    return m;
}

glm::vec3 Box::collide(glm::vec3 prev, glm::vec3 pos) {
    glm::vec3 d;
    float arr[6], val;

    d = pos - prev;

    arr[0] = (max.x - prev.x) / d.x;
    arr[1] = (min.x - prev.x) / d.x;
    arr[2] = (max.y - prev.y) / d.y;
    arr[3] = (min.y - prev.y) / d.y;
    arr[4] = (max.z - prev.z) / d.z;
    arr[5] = (min.z - prev.z) / d.z;

    val = smallest_positive_valid(6, arr, prev, d, min, max);
    if (val < 0.f) {
        val = -0.0001f;
    }
    return prev + val * d + getNormal(prev + val * d) * 0.001f;
}

bool Box::check_collide(glm::vec3 prev, glm::vec3 pos) {
    return sgn(prev) != sgn(pos) || 
     ((pos.x == min.x || pos.x == max.x) &&
     (pos.y == min.y || pos.y == max.y) &&
     (pos.z == min.z || pos.z == max.z));
      
}

float min_val(glm::vec3 v) {
    float m = v.x;

    /* uses short circuiting to prevent
     * setting m to a larger value
     */
    (m > v.y) && (m = v.y);
    (m > v.z) && (m = v.z);
    return m;
}

float max_val(glm::vec3 v) {
    float m = v.x;

    /* uses short circuiting to prevent
     * setting m to a lower value
     */
    (m < v.y) && (m = v.y);
    (m < v.z) && (m = v.z);
    return m;
}

int Box::sgn(glm::vec3 p) {
    if (p.x > max.x ||
     p.y > max.y ||
     p.z > max.z ||
     p.x < min.x ||
     p.y < min.y ||
     p.z < min.z)
        return 1;

    /*if ((p.x == max.x || p.x == min.x) &&
     (p.y == max.y || p.y == min.y) &&
     (p.z == max.z || p.z == min.z))
       return 0;*/

    return -1;
}

glm::vec3 maximize(glm::vec3 v) {
    if (fabs(v.x) >= fabs(v.y) && fabs(v.x) >= fabs(v.z))
        return glm::vec3((float) INT_SGN(v.x), 0.f, 0.f);
    else if (fabs(v.y) >= fabs(v.z)) 
        return glm::vec3(0.f, (float) INT_SGN(v.y), 0.f);
    else
        return glm::vec3(0.f, 0.f, (float) INT_SGN(v.z));
}

glm::vec3 Box::getNormal(glm::vec3 p) {
    glm::vec3 n;

    if (p.x - min.x < 0.01f && p.x - min.x > -0.01f)
        n.x = 1.f;
    else if (p.x - max.x < 0.01f && p.x - max.x > -0.01f)
        n.x = -1.f;
    if (p.y - min.y < 0.01f && p.y - min.y > -0.01f)
        n.y = 1.f;
    else if (p.y - max.y < 0.01f && p.y - max.y > -0.01f)
        n.y = -1.f;
    if (p.z - min.z < 0.01f && p.z - min.z > -0.01f)
        n.z = 1.f;
    else if (p.z - max.z < 0.01f && p.z - max.z > -0.01f)
        n.z = -1.f;

    return glm::normalize(n);
}
